﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace PrisonStep
{
    public class Gort
    {
        #region MemberVariables

        public enum States { Stance, StanceStart, Walkstart, WalkLoopStart, Walkloop, StartSalute, Salute, FinishedSalute, Catch, Eat, FinishedEating, StartEating};

        /// <summary>
        /// The animation model for the aliens
        /// </summary>
        private AnimatedModel mModel;

        /// <summary>
        /// Our Game
        /// </summary>
        private PrisonGame mGame;

        /// <summary>
        /// Player location in the prison. Only x/z are important. y still stay zero
        /// unless we add some flying or jumping behavior later on.
        /// </summary>
        private Vector3 location = new Vector3(275, 0, 1053);

        /// <summary>
        /// The player orientation as a simple angle
        /// </summary>
        private float orientation = 1.6f;

        /// <summary>
        /// The player transformation matrix. Places the player where they need to be.
        /// </summary>
        private Matrix transform;

        /// <summary>
        /// The rotation rate in radians per second when player is rotating
        /// </summary>
        private float panRate = 2;

        /// <summary>
        /// The player move rate in centimeters per second
        /// </summary>
        private float moveRate = 500;

        /// <summary>
        /// Our initial state
        /// </summary>
        private States mState = States.StanceStart;

        /// <summary>
        /// The timer will fire every 5 seconds or so
        /// </summary>
        private float mSpitTimer = 5.0f;

        private BoundingCylinder mBoundingCyl = null;

        private Spit mSpit;

        private string LastRegion;

        #endregion

        #region Properties

        public BoundingCylinder BoundingCyl { get { ComputeBoundingCylinder();  return mBoundingCyl; } }

        public float SlimeWeight { get { return mModel.SlimeWeight; } set { mModel.SlimeWeight = value; } }

        public Matrix HandTransform { get { mModel.ComputeAbsoluteTransforms(); return mModel.AbsoTransforms("Bip01 L Finger0") * transform; } }

        public States State { get { return mState; } set { mState = value; } }

        public Matrix HeadTransform { get { mModel.ComputeAbsoluteTransforms(); return mModel.AbsoTransforms("Bip01 Head") * transform * Matrix.CreateRotationY(- (float)Math.PI / 2); } }
        #endregion

        public Gort(PrisonGame aGame)
        {
            this.mGame = aGame;
            mModel = new AnimatedModel(this.mGame, "Gort");
            mModel.AddAssetClip("catcheat", "Gort-catcheat");
            mModel.AddAssetClip("stance", "Gort-stance");
            mModel.AddAssetClip("salute", "Gort-salute");
            mModel.AddAssetClip("walkloop", "Gort-walkloop");
            mModel.AddAssetClip("walkstart", "Gort-walkstart");
            SetPlayerTransform();
            mModel.Skinned = true;
            mModel.Region = "R_Section1";
            mModel.Adjacent = 1;
            

            this.mSpit = new Spit(aGame);

            mBoundingCyl = new BoundingCylinder(location, 192, 30);
        }
        private void SetPlayerTransform()
        {
            transform = Matrix.CreateRotationY(orientation);
            transform.Translation = location;
        }


        public void LoadContent(ContentManager aContent)
        {
            mModel.LoadContent(aContent);
            
            mSpit.LoadContent(aContent);
        }

        /// <summary>
        /// This function is called to update this component
        /// to the current game time
        /// </summary>
        /// <param name="aGameTime"></param>
        public void Update(GameTime aGameTime)
        {
            double lDeltaTotal = aGameTime.ElapsedGameTime.TotalSeconds;

            do
            {
                double lDelta = lDeltaTotal;

                Vector3 lInterVect = new Vector3();
                bool lIncomingPie = mGame.Victoria.BazookaProp.PieFire.IncomingPie(BoundingCyl, ref lInterVect);
                lIncomingPie = false;
                switch (mState)
                {
                    case States.StanceStart:
                    {
                        if (lIncomingPie)
                        {
                            mModel.PlayClip("catcheat").Speed = 2;
                            mState = States.Catch;
                        }
                        else
                        {
                            location.Y = 0;
                            mModel.PlayClip("stance");
                            mState = States.Stance;
                        }
                        break;
                    }


                    case States.Stance:
                    {
                        mModel.PlayClip("walkstart");
                        mState = States.Walkstart;

                        break;
                    }
                    case States.Walkstart:
                    {
                        if (mModel.FinishedAnimation())
                        {
                            location.Y = 0;
                            mModel.PlayClip("walkloop");
                            mState = States.WalkLoopStart;
                        }

                        break;
                    }
                    case States.WalkLoopStart:
                    {
                        location.Y = 0;

                        lDelta = 0;
                        mState = States.Walkloop;
                        break;
                    }
                    case States.Walkloop:
                    {
                        if (lIncomingPie)
                        {
                            mModel.PlayClip("catcheat").Speed = 1;
                            mState = States.Catch;
                        }

                        if (mSpitTimer <= 0)
                        {
                            mSpitTimer = 5.0f;
                            Spit();
                            mState = States.StartSalute;
                        }
                        mSpitTimer -= (float)lDeltaTotal;
                        if (mModel.FinishedAnimation())
                        {
                            mModel.PlayClip("walkloop");
                            mState = States.WalkLoopStart;
                        }
                        break;
                    }

                    case States.StartSalute:
                    {
                        if (lIncomingPie)
                        {
                            mModel.PlayClip("catcheat").Speed = 1;
                            mState = States.Catch;
                        }
                        mModel.PlayClip("salute");
                        mState = States.Salute;
                        break;
                    }
                    case States.Salute:
                    {
                        if (lIncomingPie)
                        {
                            mModel.PlayClip("catcheat").Speed = 1;
                            mState = States.Catch;
                        }

                        if (mModel.FinishedAnimation())
                            mState = States.FinishedSalute;

                        break;
                    }

                    case States.FinishedSalute:
                    {
                        if (lIncomingPie)
                        {
                            mModel.PlayClip("catcheat").Speed = 1;
                            mState = States.Catch;
                        }
                        mModel.PlayClip("stance");
                        mState = States.StanceStart;
                        break;
                    }
                    case States.Catch:
                    {
                        if (mModel.Clip.Time >= 1.0f)
                            mModel.Clip.Speed = 0;

                        if (!lIncomingPie)
                            mState = States.StanceStart;

                        break;
                    }
                    case States.Eat:
                    {
                        mModel.Clip.Speed = 1.0f;
                        mGame.Victoria.BazookaProp.PieFire.GortPie.orientation =  Matrix.CreateRotationX((float)Math.PI/2) * HandTransform;
                        if (mModel.Clip.Time > 2.35f)
                        {
                            mGame.Victoria.BazookaProp.PieFire.GortPie = null;
                            mState = States.FinishedEating;
                        }

                        break;
                    }
                    case States.FinishedEating:
                    {
                        if (mModel.FinishedAnimation())
                            mState = States.StanceStart;
                        break;
                    }
                    case States.StartEating:
                    {
                        mModel.PlayClip("catcheat").Speed = 1;
                        mState = States.Eat;
                        break;
                    }
                }
                mModel.Update(lDelta);

                //
                // Part 1:  Compute a new orientation
                //

                Matrix lDeltaMatrix = mModel.DeltaMatrix;
                float lDeltaAngle = (float)Math.Atan2(lDeltaMatrix.Backward.X, lDeltaMatrix.Backward.Z);
                float lNewOrientation = orientation + panRate * (float)lDeltaTotal;


                //
                // Part 2:  Compute a new location
                //

                // We are likely rotated from the angle the model expects to be in
                // Determine that angle.
                Matrix lRootMatrix = mModel.RootMatrix;
                float lActualAngle = (float)Math.Atan2(lRootMatrix.Backward.X, lRootMatrix.Backward.Z);
                Vector3 lNewLocation = location + Vector3.TransformNormal(mModel.mDeltaPosition, Matrix.CreateRotationY(lNewOrientation - lActualAngle));

                if (mState != States.StartSalute && mState != States.Salute && mState != States.Catch && 
                    mState != States.Eat && mState != States.FinishedEating)
                    orientation = lNewOrientation;

                Vector3 lNormal = Vector3.Zero;
                string region = mGame.TestRegion(lNewLocation, ref lNormal);

                bool lCollision = TestForCollision(region);

                if (LastRegion == "")
                    LastRegion = region;

                if (region != "" && lCollision)
                    location = lNewLocation;

                SetPlayerTransform();

                lDeltaTotal -= lDelta;
            }
            while (lDeltaTotal > 0);
        }

        /// <summary>
        /// This function is called to draw this game component
        /// </summary>
        /// <param name="aGraphics">Devicet to draw the model on.</param>
        /// <param name="aGameTime">Currentgame time.</param>
        /// <param name="aTransform">Transform that puts the model where we want it.</param>
        public void Draw(GraphicsDeviceManager aGraphics, GameTime aGameTime)
        {
            mModel.Draw(aGraphics, aGameTime, transform);
        }

        private void ComputeBoundingCylinder()
        {
            mBoundingCyl.Location = location;
        }

        private void Spit()
        {
            mSpit.AddSpit(HeadTransform);
        }

        private bool TestForCollision(string aRegion)
        {
            bool lTemp = false;

            //Stops if it hits a shut door
            if (aRegion.StartsWith("R_Door"))
            {
                foreach (PrisonModel lModel in mGame.Models)
                {
                    int lSection = (int)Char.GetNumericValue(aRegion[aRegion.Length - 1]);
                    bool lTemp2 = lModel.CheckForOpenDoors(lSection, transform);

                    lTemp = lTemp || lTemp2;
                }
            }
            else
                lTemp = true;

            if (aRegion.StartsWith("W_") || aRegion == "")
                lTemp = false;



            return lTemp;
        }
    }
}